using System;
using System.Xml.Serialization;
using System.Collections.Generic;
using System.Text;
using System.Net;
using System.Configuration;

using common = Microsoft.ConnectionEngine.Common;
using ws = Microsoft.ConnectionEngine.Adapters.ConnectionEngineAdapter;
using dao = Microsoft.ConnectionEngine.Registers.Core.ServiceProvider.Common;

using Microsoft.Practices.EnterpriseLibrary.Common.Configuration;
using Microsoft.Practices.EnterpriseLibrary.Common.Configuration.ObjectBuilder;
using Microsoft.Practices.EnterpriseLibrary.ExceptionHandling;
using Microsoft.Practices.EnterpriseLibrary.ExceptionHandling.Configuration;
using Microsoft.Practices.EnterpriseLibrary.Logging;
using Microsoft.Practices.EnterpriseLibrary.Logging.Configuration;
using System.Security.Cryptography.X509Certificates;
using System.Reflection;
using System.Xml;

//Modified Accessibility for methods & private members from internal/private to protected
//These changes are required for WCF enablement of SystemAdapterBase.
//Accessibility changes listed in HCE 2.1 WCF Enablement Design Document

namespace Microsoft.ConnectionEngine.Adapters
{
    /// <summary>
    /// Represents the foundation class for Adapters.
    /// </summary>
    public abstract class SystemAdapterBase
    {
        #region Private and Protected Properties
//        private const string GENERAL_ERROR_UNHANDLED = "In construction, there was an unhandled error with this Adapter.";
        private const string GENERAL_ERROR_MESSAGE_CREATION = "A problem occurred creating a Connection Engine message.";
        private const string GENERAL_ERROR_CANT_LOAD_PROVIDER = "An error ocurred loading Allowed Service Providers.";
        static private string privateKey;
        static private dao.ReferenceData refData;
        private dao.ServiceProviderList providerList;
        static private dao.ServiceProvider systemProvider;
        private string connectionEngineWebServiceURL;
        private System.Net.NetworkCredential connectionEngineCredentials;
        private string serviceProviderRegisterServiceProviderID;
        protected string serviceProviderRegisterEncryptionType;
        private string systemAdapterClientCertificate;
        protected string serviceProviderRegisterPublicKey;
        private string messageHandlerListFile;
        private System.Collections.Specialized.NameValueCollection interactiveSessionList;

        /// <summary>
        /// Represents the method that will handle a message type
        /// </summary>
        /// <param name="xmlMessage">An xml document representing the payload this handler should process.</param>
        /// <param name="resultXmlMessage">An xml document representing the payload this handler generates.</param>
        /// <param name="resultMsgType">The message type that represents the generated payload.</param>
        protected delegate void MessageHandlerDelegate(System.Xml.XmlDocument xmlMessage, out System.Xml.XmlDocument resultXmlMessage, out int resultMsgType);

        /// <summary>
        /// The internal representation of a list containing a Message Handler Delegate for each message type this Adapter is able to process.
        /// </summary>
        internal static List<common.MessageTypeHandler> messageHandlerList;

        /// <summary>
        /// A list containing a Message Handler Delegate for each message type this Adapter is able to process.
        /// </summary>
        protected List<common.MessageTypeHandler> MessageHandlerList
        {
            get
            {
                if (messageHandlerList == null)
                    messageHandlerList = LoadMessageHandlerList();
                return messageHandlerList; 
            }
        }
       
        /// <summary>
        /// The private encryption key used by the Adapter.
        /// </summary>
        private static string PrivateKey
        {
            get { return privateKey; }
            set { privateKey = value; }
        }

        /// <summary>
        /// A set of common data used by the Adapter.
        /// </summary>
        protected dao.ReferenceData RefData
        {
            get
            {
                if (refData == null)
                    refData = GetReferenceData();
                return refData; 
            }
            set { refData = value; }
        }

        /// <summary>
        /// A collection of Interactive Session URLs this Adapter is allowed to generate.
        /// </summary>
        protected System.Collections.Specialized.NameValueCollection InteractiveSessionList
        {
            get
            {
                if (interactiveSessionList == null)
                    interactiveSessionList = GetInteractiveSessionList();
                return interactiveSessionList;
            }
        }


        /// <summary>
        /// Represents the Connected System associated to this Adapter.
        /// </summary>
        protected static dao.ServiceProvider SystemProvider
        {
            get { return systemProvider; }
            set { systemProvider = value; }
        }

        /// <summary>
        /// A collection of Service Providers this Adapter's Connected System is allowed to contact.
        /// </summary>
        protected dao.ServiceProviderList ProviderList
        {
            get
            {
                if (providerList == null)
                    providerList = GetAllowedProviderList();
                return providerList;
            }
            set
            {
                providerList = value;
            }

        }

        #endregion

        #region Public & Protected Methods

        /// <summary>
        /// Creates a new instance of a Adapter Base.
        /// </summary>
        public SystemAdapterBase()
        {
            //Work Item ID: 1268
            //Modified By: SachinJo
            //error_location is assigned value but never used, temporarily commenting the error_location code block
            //once error logging is implemented this code needs to be un-commented.
            //string error_location = "";
            //error_location = "Loading Connection Engine Web Service URL";
            string hceserviceurl = System.Configuration.ConfigurationManager.AppSettings["ConnectionEngine.WebServiceURL"];

            //error_location = "Loading ConnectionEngine Credentials";
            string hceusername = string.Empty;
            string hcepassword = string.Empty;
            if (!(String.IsNullOrEmpty(System.Configuration.ConfigurationManager.AppSettings["ConnectionEngine.UserName"])) &&
            !(String.IsNullOrEmpty(System.Configuration.ConfigurationManager.AppSettings["ConnectionEngine.Password"])))
            {
                hceusername = System.Configuration.ConfigurationManager.AppSettings["ConnectionEngine.UserName"];
                hcepassword = System.Configuration.ConfigurationManager.AppSettings["ConnectionEngine.Password"];
            }

            //error_location = "Loading Health Services Directory Service Provider ID";
            string hsdserviceproviderid = System.Configuration.ConfigurationManager.AppSettings["ServiceProviderRegister.ServiceProviderID"];

            //error_location = "Loading Health Services Directory Encryption Type";
            string hsdencryptiontype = System.Configuration.ConfigurationManager.AppSettings["ServiceProviderRegister.EncryptionType"];

            //error_location = "Loading Health Services Directory PublicKey";
            string hsdpublickey = System.Configuration.ConfigurationManager.AppSettings["ServiceProviderRegister.PublicKey"];

            //error_location = "Loading Provider ID";
            string csaserviceproviderid = System.Configuration.ConfigurationManager.AppSettings["SystemAdapter.ServiceProviderID"];

            //error_location = "Loading Provider Web Service URL";
            string csawebserviceurl = System.Configuration.ConfigurationManager.AppSettings["SystemAdapter.WebServiceURL"];

            //error_location = "Loading Provider Encryption Algorithm";
            string csaencryptionalgorithm = System.Configuration.ConfigurationManager.AppSettings["SystemAdapter.EncryptionType"];

            //error_location = "Loading Provider Public Key Location";
            string csapublickey = System.Configuration.ConfigurationManager.AppSettings["SystemAdapter.PublicKey"];

            //error_location = "Loading Provider Private Key Location";
            string csaprivatekey = System.Configuration.ConfigurationManager.AppSettings["SystemAdapter.PrivateKey"];

            //error_location = "Loading Provider Client Certifcate Name";
            string csaclientcertificate = System.Configuration.ConfigurationManager.AppSettings["SystemAdapter.ClientCertificateName"]; ;

            //error_location = "Loading Message Handler List File";
            string messagehandlerlist = System.Configuration.ConfigurationManager.AppSettings["SystemAdapter.MessageHandlerList"];

            Initialize(hceserviceurl, hceusername, hcepassword, hsdserviceproviderid, hsdencryptiontype, hsdpublickey, csaserviceproviderid, csawebserviceurl, csaencryptionalgorithm, csapublickey, csaprivatekey, csaclientcertificate, messagehandlerlist);
        }

        /// <summary>
        /// Creates a new instance of a Adapter Base based on the parameters passed as argument.
        /// </summary>
        /// <param name="connectionenginewebserviceurl">
        /// Represents the Routing Services entry point web service.
        /// </param>
        /// <param name="connectionengineusername">
        /// Represents an optional username to use as credentials when connecting to the Routing Services.
        /// </param>
        /// <param name="connectionenginepassword">
        /// Represents an optional password to use as credentials when connecting to the Routing Services.
        /// </param>
        /// <param name="serviceproviderregisterserviceproviderid">
        /// Represents the unique identifier of the Service Provider Register.
        /// </param>
        /// <param name="serviceproviderregisterencryptiontype">
        /// Represents the encryption type used by Service Provider Register.
        /// </param>
        /// <param name="serviceproviderregisterpublickey">
        /// Represents the public key used to encrypt messages sent to Service Provider Register.
        /// </param>
        /// <param name="systemadapterserviceproviderid">
        /// Represents the unique identifier of the Service Provider associated with the Adapter.
        /// </param>
        /// <param name="systemadapterwebserviceurl">
        /// An optional string representing the entry point web services used by Adapter.
        /// </param>
        /// <param name="systemadapterencryptionalgorithm">
        /// Represents the encryption algorithm used by the Adapter.
        /// </param>
        /// <param name="systemadapterpublickey">
        /// Represents the public key used by the Adapter.
        /// </param>
        /// <param name="systemadapterprivatekey">
        /// Represents the private key used by the Adapter.
        /// </param>
        /// <param name="systemadapterclientcertificate">
        /// Represents the client certificate that uniquely identifies the Adapter when communicating with the Routing Service through a secure channel.
        /// </param>
        /// <param name="messagehandlerlist">
        /// Represents the list of message handlers used by the Adapter to process received messages.
        /// </param>
        public SystemAdapterBase(
            string connectionenginewebserviceurl,
            string connectionengineusername,
            string connectionenginepassword,
            string serviceproviderregisterserviceproviderid,
            string serviceproviderregisterencryptiontype,
            string serviceproviderregisterpublickey,
            string systemadapterserviceproviderid,
            string systemadapterwebserviceurl,
            string systemadapterencryptionalgorithm,
            string systemadapterpublickey,
            string systemadapterprivatekey,
            string systemadapterclientcertificate,
            string messagehandlerlist)
        {
            Initialize(connectionenginewebserviceurl, connectionengineusername, connectionenginepassword, serviceproviderregisterserviceproviderid, serviceproviderregisterencryptiontype, serviceproviderregisterpublickey, systemadapterserviceproviderid, systemadapterwebserviceurl, systemadapterencryptionalgorithm, systemadapterpublickey, systemadapterprivatekey, systemadapterclientcertificate, messagehandlerlist);
        }

        /// <summary>
        /// Receive a Connection Engine Message from another Connected System, through the Routing Services.
        /// </summary>
        /// <param name="msg">The Connection Engine Message being received from another Connected System.</param>
        /// <returns>
        /// Returns a Connection Engine Message that includes Acknowledgement status, confirming if the message was sent successfully or not,
        /// as well as the resulting payload content, when necessary.
        /// </returns>
        public common.ConnectionEngineMessage ReceiveConnectionEngineMessage(common.ConnectionEngineMessage msg)
        {
            common.ConnectionEngineMessage resultMessage = msg;
            string payload = msg.Body.Payload.Content;
            int resultInteractionType = msg.Header.InteractionType;
            System.Xml.XmlDocument payloadXML = new System.Xml.XmlDocument();
            try
            {
                //Decrypt Payload
                common.Crypto.EncryptedDataInfo encryptedData = new common.Crypto.EncryptedDataInfo(msg.Body.Payload.Content,
                                                                                                        msg.Body.Payload.IV,
                                                                                                        msg.Body.Payload.Key);
                payload = DecryptPayload(encryptedData, GetCryptoType(SystemProvider.EncryptionAlgorithm));

                //Validate XML
                payloadXML.LoadXml(payload);
                ValidatePayloadSchema(payloadXML, Convert.ToInt32(msg.Header.InteractionType));

                //Send to ProcessMessage 

                System.Xml.XmlDocument resultPayloadXml = new System.Xml.XmlDocument();

                if (IsNotificationMessage(msg.Header.InteractionType))
                {
                    ProcessNotification(payloadXML, out resultPayloadXml, out resultInteractionType);
                }
                else
                {
                    ProcessMessage(payloadXML, msg.Header.InteractionType, out resultPayloadXml, out resultInteractionType);
                }
                if (resultInteractionType > 0 &&
                    resultInteractionType != msg.Header.InteractionType)
                {
                    ValidatePayloadSchema(resultPayloadXml, resultInteractionType);

                    dao.ServiceProvider systemProviderDestination = GetDestinationProvider(msg.Header.Sender.OrganisationID);

                    //Encrypt Payload
                    encryptedData = EncryptPayload(resultPayloadXml.InnerXml, systemProviderDestination.EncryptionAlgorithm, systemProviderDestination.PublicKey);

                    //Construct Message
                    resultMessage = ConstructConnectionEngineMessage(systemProviderDestination.EncryptionAlgorithm,
                                                                                                                        SystemProvider.ServiceProviderID,
                                                                                                                        systemProviderDestination.ServiceProviderID,
                                                                                                                        resultInteractionType,
                                                                                                                        msg.Header.Conversation.ConversationID,
                                                                                                                        msg.Header.ID,
                                                                                                                        encryptedData
                                                                                                                        );
                }

                resultMessage.Header.Status.Code = (int)common.SystemAdapterExceptionCause.Ok;
                resultMessage.Header.Status.Description = GetStatusDescription(0);

            }
            catch (common.SystemAdapterException sysEx)
            {
                ExceptionPolicy.HandleException(sysEx, "HCE Policy");
                resultMessage.Header.Status.Code = (int)sysEx.Cause;
                resultMessage.Header.Status.Description = GetStatusDescription((int)resultMessage.Header.Status.Code);
                if (sysEx.InnerException != null)
                    resultMessage.Header.Status.Details = sysEx.InnerException.Message;
            }
            catch (Exception ex) // Catches unhandled errors.
            {
                ExceptionPolicy.HandleException(ex, "HCE Policy");
                resultMessage.Header.Status.Code = (int)common.SystemAdapterExceptionCause.Unknown;
                resultMessage.Header.Status.Description = GetStatusDescription(99);
                if (ex.InnerException != null)
                    resultMessage.Header.Status.Details = ex.InnerException.Message;
            }
            return resultMessage;
        }

        /// <summary>
        /// Sends a Connection Engine Message to another Connected System, through the Routing Services.
        /// </summary>
        /// <param name="objMessage">An object representing the message to be parsed by the Adapter, becoming a Connection Engine Message.</param>
        /// <param name="msgType">The object representation of the message type that the Connected System wishes to send.</param>
        /// <param name="destinationId">The unique identifier of the destination Connected System.</param>
        /// <returns>
        /// Returns a Connected Engine Message that includes Acknowledgement status, confirming if the message was sent successfully or not,
        /// as well as the resulting payload content, when necessary.
        /// </returns>
        public common.ConnectionEngineMessage SendConnectionEngineMessage(Object objMessage, dao.MessageType msgType, string destinationId)
        {
            return SendConnectionEngineMessage(objMessage, (int)msgType.MessageTypeID, Guid.Empty, destinationId);
        }

        /// <summary>
        /// Sends a Connection Engine Message to another Connected System, through the Routing Services.
        /// </summary>
        /// <param name="objMessage">An object representing the message to be parsed by the Adapter, becoming a Connection Engine Message.</param>
        /// <param name="msgType">The unique identifier representing the message type that the Connected System wishes to send.</param>
        /// <param name="destinationId">The unique identifier of the destination Connected System.</param>
        /// <returns>
        /// Returns a Connection Engine Message that includes Acknowledgement status, confirming if the message was sent successfully or not,
        /// as well as the resulting payload content, when necessary.
        /// </returns>
        public common.ConnectionEngineMessage SendConnectionEngineMessage(Object objMessage, int msgType, string destinationId)
        {
            return SendConnectionEngineMessage(objMessage, msgType, Guid.Empty, destinationId);
        }

        /// <summary>
        /// Sends a Connection Engine Message to another Connected System, through the Routing Services.
        /// </summary>
        /// <param name="objMessage">An object representing the message to be parsed by the Adapter, becoming a Connection Engine Message.</param>
        /// <param name="msgType">The unique identifier representing the message type the Connected System wishes to send.</param>
        /// <param name="conversationId">A unique identifier representing the Conversation thread related to the message being sent.</param>
        /// <param name="destinationId">The unique identifier of the destination Connected System.</param>
        /// <returns>
        /// Returns a Connection Engine Message that includes Acknowledgement status, confirming if the message was sent successfully or not,
        /// as well as the resulting payload content, when necessary.
        /// </returns>
        public common.ConnectionEngineMessage SendConnectionEngineMessage(Object objMessage, int msgType, Guid conversationId, string destinationId)
        {
            // Use CreateConnectionAdapterMessage to create new object because the webservice constructor doesn't intantiate all the properties.
            ws.ConnectionEngineMessage resultMessage = CreateConnectionAdapterMessage();

            try
            {
                System.Xml.XmlDocument payloadXML = new System.Xml.XmlDocument();
                dao.ServiceProvider systemProviderDestination = GetDestinationProvider(destinationId);

                //AssembleMessage
                payloadXML = AssembleMessage(objMessage, msgType);

                //ValidateMessage
                ValidatePayloadSchema(payloadXML, msgType);

                //EncryptMessage
                common.Crypto.EncryptedDataInfo encryptedData = EncryptPayload(payloadXML.InnerXml, systemProviderDestination.EncryptionAlgorithm, systemProviderDestination.PublicKey);

                //Construct Message
                ws.ConnectionEngineMessage msg = ConstructWebServiceMessage(systemProviderDestination.EncryptionAlgorithm,
                                                                                                                    SystemProvider.ServiceProviderID,
                                                                                                                    destinationId,
                                                                                                                    msgType,
                                                                                                                    conversationId,
                                                                                                                    Guid.Empty,
                                                                                                                    encryptedData
                                                                                                                    );
                //Sendmessage
                resultMessage = SendMessage(msg);
                if (resultMessage.Header.Status.Code == 0)
                {
                    if (resultMessage.Header.InteractionType > 0 &&
                        resultMessage.Header.InteractionType != msg.Header.InteractionType)
                    {
                        common.Crypto.EncryptedDataInfo encData = new common.Crypto.EncryptedDataInfo(resultMessage.Body.Payload.Content,
                                                                                        resultMessage.Body.Payload.IV,
                                                                                        resultMessage.Body.Payload.Key);
                        string payload = DecryptPayload(encData, GetCryptoType(SystemProvider.EncryptionAlgorithm));

                        resultMessage.Body.Payload.EncryptionType = ws.CryptoTypes.encTypeNone;
                        resultMessage.Body.Payload.IV = string.Empty;
                        resultMessage.Body.Payload.Key = string.Empty;
                        resultMessage.Body.Payload.Content = payload;
                    }
                }

            }
            catch (common.SystemAdapterException sysEx)
            {
                ExceptionPolicy.HandleException(sysEx, "HCE Policy");
                resultMessage.Header.Status.Code = (int)sysEx.Cause;
                resultMessage.Header.Status.Description = GetStatusDescription((int)resultMessage.Header.Status.Code);
                if (sysEx.InnerException != null)
                    resultMessage.Header.Status.Details = sysEx.InnerException.Message;

            }
            catch (Exception ex) // Catches unhandled errors.
            {
                ExceptionPolicy.HandleException(ex, "HCE Policy");
                resultMessage.Header.Status.Code = (int)common.SystemAdapterExceptionCause.Unknown;
                resultMessage.Header.Status.Description = GetStatusDescription(99);
                if (ex.InnerException != null)
                    resultMessage.Header.Status.Details = ex.InnerException.Message;

            }

            common.ConnectionEngineMessage finalResult = ConvertWSMessageToCommonMessage(resultMessage);

            return finalResult;
        }

        /// <summary>
        /// Gets the URL for an HTTP Interactive Session with a Destination Service Provider, based on a MessageType and a list of arguments.
        /// </summary>
        /// <param name="serviceproviderid">The Destination Service Provider unique identifier</param>
        /// <param name="messagetypeid">The message type used to generate the arguments of the interactive session.</param>
        /// <param name="args">
        /// A collection of arguments needed to generate the interactive session.
        /// <remarks>
        /// This is a List of KeyValuePairs to allow for serialization.
        /// The original idea was to use a NameValueCollection, but this object is not xml serializable.
        /// </remarks>
        /// </param>
        /// <returns>Returns the generated interactive session URL.</returns>
        public string GetInteractiveSessionInformation(string serviceproviderid, int messagetypeid, List<common.KeyValueItem> args)
        {
            try
            {
                string argumentlist = string.Empty;
                for (int x = 0; x < args.Count; x++)
                    argumentlist += String.Format("<{0}>{1}</{0}>", args[x].Key, args[x].Value);

                string interactivesessionmessage = String.Format(@"<InteractiveSession xmlns=""http://Microsoft.CollaborationEngine/Schemas""><MessageTypeID>{0}</MessageTypeID><ServiceProviderID>{1}</ServiceProviderID><Arguments>{2}</Arguments></InteractiveSession>",
                    messagetypeid, SystemProvider.ServiceProviderID, argumentlist);
                System.Xml.XmlDocument doc = new System.Xml.XmlDocument();
                doc.LoadXml(interactivesessionmessage);
                if (ValidatePayloadSchema(doc, messagetypeid))
                {
                    argumentlist = string.Empty;
                    //dao.ServiceProvider destinationprovider = GetDestinationProvider(serviceproviderid);
                    argumentlist = String.Format("messagetypeid={0}&serviceproviderid={1}", messagetypeid, SystemProvider.ServiceProviderID);
                    for(int x = 0; x < args.Count; x++)
                        argumentlist += String.Format("&{0}={1}", args[x].Key, args[x].Value);
                    string interactivesession = String.Format("{0}?{1}", InteractiveSessionList[serviceproviderid], argumentlist);
                    return interactivesession;

                }
                else
                {
                    throw new common.SystemAdapterException(common.SystemAdapterExceptionCause.Hl7PlayloadCreation, "Error Creating Iteractive Session Payload", null);
                }
            }
            catch (common.SystemAdapterException sysEx)
            {

                throw sysEx;
            }
            catch (System.Exception ex)
            {
                throw new common.SystemAdapterException(common.SystemAdapterExceptionCause.Unknown, "An unexpecte error happened during Interactive Session creation", ex);
            }
        }
        /// <summary>
        /// Creates a new Connection Engine compatible payload based on a data specific to a Connected System and a Message Type.
        /// </summary>
        /// <param name="objMessage">An object representing the Connected System data to be sent through the Routing Service.</param>
        /// <param name="msgType">The Message Type that defines the Payload to be created.</param>
        /// <returns>Returns an XML Document representing the Connection Engine compatible payload.</returns>
        protected abstract System.Xml.XmlDocument AssembleMessage(Object objMessage, int msgType);

        /// <summary>
        /// Gathers the common data needed by the Adapter to perform its activities.
        /// </summary>
        /// <returns>Returns an object representation of the Reference Data package.</returns>
        protected virtual dao.ReferenceData GetReferenceData()
        {
            dao.ReferenceData refDataResult = null;
            System.Xml.XmlDocument serviceproviderregisterkey = new System.Xml.XmlDocument();
            serviceproviderregisterkey.Load(serviceProviderRegisterPublicKey);
            string payloadRefData = @"<HSDReferenceDataRequest  xmlns=""http://Microsoft.ConnectionEngine/Schemas/ServiceProviderRegister"" />";
            
            common.Crypto.EncryptedDataInfo encryptedData = EncryptPayload(payloadRefData,
                                                                                     serviceProviderRegisterEncryptionType,
                                                                                     serviceproviderregisterkey.OuterXml);

            ws.ConnectionEngineMessage request = new ws.ConnectionEngineMessage();
            request = ConstructWebServiceMessage(serviceProviderRegisterEncryptionType,
                                            SystemProvider.ServiceProviderID,
                                            serviceProviderRegisterServiceProviderID,
                                            (int)dao.MessageTypes.HSDReferenceDataRequest,
                                            Guid.Empty,
                                            Guid.Empty,
                                            encryptedData);

            ws.ConnectionEngineMessage result = SendMessage(request);

            if (result.Header.Status.Code == 0)
            {
                common.Crypto.EncryptedDataInfo encryptedResultData = new common.Crypto.EncryptedDataInfo(result.Body.Payload.Content,
                                                                                                        result.Body.Payload.IV,
                                                                                                        result.Body.Payload.Key);

                string refDataXml = DecryptPayload(encryptedResultData, GetCryptoType(SystemProvider.EncryptionAlgorithm));
                System.Xml.Serialization.XmlSerializer serializer = new System.Xml.Serialization.XmlSerializer(typeof(dao.ReferenceData));
                System.IO.TextReader tr = new System.IO.StringReader(refDataXml);
                refDataResult = (dao.ReferenceData)serializer.Deserialize(tr);
                tr.Dispose();
            }
            else
            {
                throw new common.SystemAdapterException(common.SystemAdapterExceptionCause.Unknown, GENERAL_ERROR_CANT_LOAD_PROVIDER, null);
            }

            return refDataResult;
        }

        /// <summary>
        /// Gets a list of Interactive Session URI this Adapter is allowed to generate. 
        /// </summary>
        /// <returns>
        /// Returns a Name Value Collection representing the list of Interactive Session URI,
        /// where the key is the Service Provider ID and the value is the interactive session URI.
        /// </returns>
        protected virtual System.Collections.Specialized.NameValueCollection GetInteractiveSessionList()
        {
            System.Collections.Specialized.NameValueCollection interactivesessionresult = null;
            System.Xml.XmlDocument hsdkey = new System.Xml.XmlDocument();
            hsdkey.Load(serviceProviderRegisterPublicKey);
            string payloadInteractiveSession = String.Format(@"<request  xmlns=""http://Microsoft.ConnectionEngine/Schemas/ServiceProviderRegister""><ServiceProviderID>{0}</ServiceProviderID></request>", SystemProvider.ServiceProviderID);

            common.Crypto.EncryptedDataInfo encryptedData = EncryptPayload(payloadInteractiveSession,
                                                                                     serviceProviderRegisterEncryptionType,
                                                                                     hsdkey.OuterXml);

            ws.ConnectionEngineMessage request = new ws.ConnectionEngineMessage();
            request = ConstructWebServiceMessage(serviceProviderRegisterEncryptionType,
                                            SystemProvider.ServiceProviderID,
                                            serviceProviderRegisterServiceProviderID,
                                            9026,
                                            Guid.Empty,
                                            Guid.Empty,
                                            encryptedData);

            ws.ConnectionEngineMessage result = SendMessage(request);

            if (result.Header.Status.Code == 0)
            {
                interactivesessionresult = new System.Collections.Specialized.NameValueCollection();
                common.Crypto.EncryptedDataInfo encryptedResultData = new common.Crypto.EncryptedDataInfo(result.Body.Payload.Content,
                                                                                                        result.Body.Payload.IV,
                                                                                                        result.Body.Payload.Key);

                string interactiveSessionXml = DecryptPayload(encryptedResultData, GetCryptoType(SystemProvider.EncryptionAlgorithm));
                XmlDocument doc = new XmlDocument();
                doc.LoadXml(interactiveSessionXml);

                XmlNamespaceManager nsmgr = new XmlNamespaceManager(doc.NameTable);
                nsmgr.AddNamespace("pnp", doc.FirstChild.NamespaceURI);

                XmlNodeList nodes = doc.SelectNodes("//pnp:InteractiveSession", nsmgr);
                foreach (XmlNode item in nodes)
                    interactivesessionresult.Add(item.ChildNodes[0].InnerText, item.ChildNodes[1].InnerText);
            }
            else
            {
                throw new common.SystemAdapterException(common.SystemAdapterExceptionCause.Unknown, "Error Loading Interactive Session List", null);
            }

            return interactivesessionresult;
        }

        /// <summary>
        /// Load a list of Connected Systems this Adapter is able to contact.
        /// This list of Connected Systems (also known as Service Providers) will allow the Adapter to find the right Public Key to use when encrypting the payload.
        /// </summary>
        /// <returns>Returns an object representation of the Service Provider List, which contains information about the Connected Systems that this Adapter can contact.</returns>
        protected virtual dao.ServiceProviderList GetAllowedProviderList()
        {
            dao.ServiceProviderList providerListResult = null;
            System.Xml.XmlDocument serviceprovideregisterkey = new System.Xml.XmlDocument();
            serviceprovideregisterkey.Load(serviceProviderRegisterPublicKey);
            string payloadServiceProviderList = String.Format(@"<request  xmlns=""http://Microsoft.ConnectionEngine/Schemas/ServiceProviderRegister""><ServiceProviderID>{0}</ServiceProviderID></request>", SystemProvider.ServiceProviderID);

            common.Crypto.EncryptedDataInfo encryptedData = EncryptPayload(payloadServiceProviderList,
                                                                                     serviceProviderRegisterEncryptionType,
                                                                                     serviceprovideregisterkey.OuterXml);

            ws.ConnectionEngineMessage request = new ws.ConnectionEngineMessage();
            request = ConstructWebServiceMessage(serviceProviderRegisterEncryptionType,
                                            SystemProvider.ServiceProviderID,
                                            serviceProviderRegisterServiceProviderID,
                                            (int)dao.MessageTypes.ServiceProviderListRequest,
                                            Guid.Empty,
                                            Guid.Empty,
                                            encryptedData);

            ws.ConnectionEngineMessage result = SendMessage(request);

            if (result.Header.Status.Code == 0)
            {
                common.Crypto.EncryptedDataInfo encryptedResultData = new common.Crypto.EncryptedDataInfo(result.Body.Payload.Content,
                                                                                                        result.Body.Payload.IV,
                                                                                                        result.Body.Payload.Key);

                string providerslistxml = DecryptPayload(encryptedResultData, GetCryptoType(SystemProvider.EncryptionAlgorithm));
                System.Xml.Serialization.XmlSerializer serializer = new System.Xml.Serialization.XmlSerializer(typeof(dao.ServiceProviderList));
                System.IO.TextReader tr = new System.IO.StringReader(providerslistxml);
                providerListResult = (dao.ServiceProviderList)serializer.Deserialize(tr);
                tr.Dispose();
            }
            else
            {
                throw new common.SystemAdapterException(common.SystemAdapterExceptionCause.Unknown, GENERAL_ERROR_CANT_LOAD_PROVIDER, null);
            }

            return providerListResult;
        }

        /// <summary>
        /// Loads a list of Adapter Message Handlers.
        /// </summary>
        /// <returns>Return an object representation of the Message Handler list.</returns>
        protected virtual List<common.MessageTypeHandler> LoadMessageHandlerList()
        {
            System.Xml.XmlDocument doc = new System.Xml.XmlDocument();
            doc.Load(messageHandlerListFile);

            List<common.MessageTypeHandler> result =
                (List<common.MessageTypeHandler>)common.Utilities.DeserializeObject(doc.OuterXml, typeof(List<common.MessageTypeHandler>));
            return result;
        }


        /// <summary>
        /// Process a Connection Engine compatible payload based on its Message Type.
        /// </summary>
        /// <param name="xmlMessage">Represents the Connection Engine compatible payload to be processed</param>
        /// <param name="msgType">The Message Type that defines the payload to be processed.</param>
        /// <param name="resultXmlMessage">An XML representation of the returned payload.</param>
        /// <param name="resultMsgType">A representation of the Message Type for the returned payload.</param>
        protected virtual void ProcessMessage(System.Xml.XmlDocument xmlMessage, int msgType, out System.Xml.XmlDocument resultXmlMessage, out int resultMsgType)
        {
            resultXmlMessage = new System.Xml.XmlDocument();
            try
            {
                // Finds the MessageTypeHandler using an anonymous method inside the Find predicate.
                common.MessageTypeHandler msgHandler = MessageHandlerList.Find(delegate(common.MessageTypeHandler node)
                            {
                                if (node.MessageType == msgType)
                                    return true;
                                else
                                    return false;
                            });

                System.Reflection.Assembly assembly = Assembly.LoadFrom(System.AppDomain.CurrentDomain.RelativeSearchPath + @"\" + msgHandler.AssemblyName);
                Type t = assembly.GetType(msgHandler.ClassName);

                MessageHandlerDelegate dlgt = (MessageHandlerDelegate)
                                               Delegate.CreateDelegate(
                                               typeof(MessageHandlerDelegate),
                                               t, msgHandler.MethodName);


                dlgt.Invoke(xmlMessage, out resultXmlMessage, out resultMsgType);
            }

            catch (System.Exception ex)
            {
                common.SystemAdapterException sysEx = new common.SystemAdapterException(
                    common.SystemAdapterExceptionCause.PayloadContentNotValid,
                    "An error occurred processing the payload.",
                    ex);
                throw sysEx;
            }
        }


        #endregion

        #region Internal Methods

        /// <summary>
        /// Creates a new empty Connection Adapter Engine Message including objects for all the properties.
        /// </summary>
        /// <returns>Returns Returns An empty Connection Engine Message with all properties objects instantiated.</returns>
        internal static ws.ConnectionEngineMessage CreateConnectionAdapterMessage()
        {
            ws.ConnectionEngineMessage resultMessage = new ws.ConnectionEngineMessage();

            // Creates Header
            resultMessage.Header = new ws.ConnectionEngineMessageHeader();
            resultMessage.Header.Conversation = new ws.ConnectionEngineConversation();
            resultMessage.Header.Receiver = new ws.ConnectionEngineEndPoint();
            resultMessage.Header.Sender = new ws.ConnectionEngineEndPoint();
            resultMessage.Header.Status = new ws.ConnectionEngineStatus();

            // Creates Body

            resultMessage.Body = new ws.ConnectionEngineMessageBody();
            resultMessage.Body.Payload = new ws.ConnectionEnginePayload();

            return resultMessage;
        }

        /// <summary>
        /// Decrypts the payload
        /// </summary>
        /// <param name="encryptedDataInfo">An object representation of the Encrypted content, containing the content, the encrypted key, and the Encrypted IV.</param>
        /// <param name="cryptoType">A representation of the Encryption Algorithm used.</param>
        /// <returns>Returns the decrpyted content.</returns>
        protected string DecryptPayload(common.Crypto.EncryptedDataInfo encryptedDataInfo, common.CryptoTypes cryptoType)
        {
            if (cryptoType == common.CryptoTypes.encTypeNone)
                return encryptedDataInfo.EncryptedData;
            else
            {
                common.Crypto ct;
                string result = string.Empty;
                try
                {
                    ct = new common.Crypto(PrivateKey);
                    result = ct.Decrypt(encryptedDataInfo, cryptoType);
                }
                catch (Exception ex)
                {
                    throw new common.SystemAdapterException(common.SystemAdapterExceptionCause.PayloadDecryption,
                        "A problem occurred during decryption of the payload", ex);
                }
                return result;
            }
        }



        /// <summary>Encrypts the payload in a format recognizable only by the Destination Connected System.
        /// </summary>
        /// <param name="payload">The payload to be encrypted.</param>
        /// <param name="encryptionalgorithm">The Encryption Algorithm preferred by the Destination Connected System.</param>
        /// <param name="publickey">The Destination Connected System's public encryption key.</param>
        /// <returns>Returns a EncryptedDataInfo object representing the encrypted payload.</returns>
        protected static common.Crypto.EncryptedDataInfo EncryptPayload(string payload, string encryptionalgorithm, string publickey)
        {
            common.Crypto ct;
            common.Crypto.EncryptedDataInfo result;
            try
            {
                ct = new common.Crypto(publickey);
                result = ct.Encrypt(payload, GetCryptoType(encryptionalgorithm));



            }
            catch (Exception ex)
            {
                throw new common.SystemAdapterException(common.SystemAdapterExceptionCause.PayloadEncryption,
                    "A problem occurred during encryption of the payload", ex);
            }
            return result;

        }

        /// <summary>
        /// Creates a Connection Engine Adapter compatible Message.
        /// </summary>
        /// <param name="encryptionalgorithm">The encryption algorithm used to encrypt the payload.</param>
        /// <param name="sourceProviderID">The identifier representing the Source Connected System.</param>
        /// <param name="destinationProviderID">The identifier representing the Destination Connected System.</param>
        /// <param name="msgType">An identifier representing the Message type being sent.</param>
        /// <param name="conversationID">The unique identifier representing the conversation thread this message belongs to.</param>
        /// <param name="orginalMessageID">The unique identifier representing the message being constructed.</param>
        /// <param name="encryptedDataInfo">An object representation of the Encrypted content, containing the content, the encrypted key, and the Encrypted IV.</param>
        /// <returns>Returns an object representing the generated Connected Engine Adapter Message.</returns>
        internal static ws.ConnectionEngineMessage ConstructWebServiceMessage(
            string encryptionalgorithm,
            string sourceProviderID,
            string destinationProviderID,
            int msgType,
            Guid conversationID,
            Guid orginalMessageID,
            common.Crypto.EncryptedDataInfo encryptedDataInfo
            )
        {
            ws.ConnectionEngineMessage newMessage = new ws.ConnectionEngineMessage();
            try
            {
                ws.ConnectionEnginePayload enginePayload = new ws.ConnectionEnginePayload();
                enginePayload.Key = encryptedDataInfo.EncryptedKey;
                enginePayload.IV = encryptedDataInfo.EncryptedIV;
                enginePayload.Content = encryptedDataInfo.EncryptedData;
                enginePayload.EncryptionType = GetWSCryptoType(encryptionalgorithm);

                newMessage.Header = new ws.ConnectionEngineMessageHeader();
                newMessage.Header.Conversation = new ws.ConnectionEngineConversation();
                newMessage.Header.Conversation.ConversationID = conversationID;
                newMessage.Header.Conversation.OriginalMessageID = orginalMessageID;

                newMessage.Header.Sender = new ws.ConnectionEngineEndPoint();
                newMessage.Header.Sender.ConnectionEngineUserID = sourceProviderID;
                newMessage.Header.Sender.OrganisationID = sourceProviderID;

                newMessage.Header.Receiver = new ws.ConnectionEngineEndPoint();
                newMessage.Header.Receiver.ConnectionEngineUserID = destinationProviderID;
                newMessage.Header.Receiver.OrganisationID = destinationProviderID;
                newMessage.Header.CreationTime = DateTime.Now;
                newMessage.Header.ID = Guid.NewGuid();
                newMessage.Header.InteractionType = msgType;

                newMessage.Header.Status = new ws.ConnectionEngineStatus();
                newMessage.Header.Status.Code = -1;
                newMessage.Header.Status.Description = string.Empty;
                newMessage.Header.Status.Details = string.Empty;

                newMessage.Body = new ws.ConnectionEngineMessageBody();
                newMessage.Body.Payload = enginePayload;
            }
            catch (Exception ex)
            {
                throw new common.SystemAdapterException(common.SystemAdapterExceptionCause.CreatingPnPMsg,
                    GENERAL_ERROR_MESSAGE_CREATION, ex);
            }

            return newMessage;
        }


        /// <summary>
        /// Creates a Connection Engine compatible Message.
        /// </summary>
        /// <param name="encryptionalgorithm">The encryption algorithm used to encrypt the payload.</param>
        /// <param name="sourceProviderID">The identifier representing the Source Connected System.</param>
        /// <param name="destinationProviderID">The identifier representing the Destination Connected System.</param>
        /// <param name="msgType">An identifier representing the Message type being sent.</param>
        /// <param name="conversationID">The unique identifier representing the conversation thread this message belongs to.</param>
        /// <param name="orginalMessageID">The unique identifier representing the message being constructed.</param>
        /// <param name="encryptedDataInfo">An object representation of the Encrypted content, containing the content, the encrypted key, and the Encrypted IV.</param>
        /// <returns>Returns an object representing the generated Connection Engine Message.</returns>
        internal static common.ConnectionEngineMessage ConstructConnectionEngineMessage(
            string encryptionalgorithm,
            string sourceProviderID,
            string destinationProviderID,
            int msgType,
            Guid conversationID,
            Guid orginalMessageID,
            common.Crypto.EncryptedDataInfo encryptedDataInfo
            )
        {
            common.ConnectionEngineMessage newMessage = new common.ConnectionEngineMessage();
            try
            {
                common.ConnectionEnginePayload enginePayload = new common.ConnectionEnginePayload();
                enginePayload.Key = encryptedDataInfo.EncryptedKey;
                enginePayload.IV = encryptedDataInfo.EncryptedIV;
                enginePayload.Content = encryptedDataInfo.EncryptedData;
                enginePayload.EncryptionType = GetCryptoType(encryptionalgorithm);

                newMessage.Header = new common.ConnectionEngineMessageHeader();
                newMessage.Header.Conversation = new common.ConnectionEngineConversation();
                newMessage.Header.Conversation.ConversationID = conversationID;
                newMessage.Header.Conversation.OriginalMessageID = orginalMessageID;

                newMessage.Header.Sender = new common.ConnectionEngineEndPoint();
                newMessage.Header.Sender.OrganisationID = sourceProviderID;

                newMessage.Header.Receiver = new common.ConnectionEngineEndPoint();
                newMessage.Header.Receiver.OrganisationID = destinationProviderID;
                newMessage.Header.CreationTime = DateTime.Now;
                newMessage.Header.ID = Guid.NewGuid();
                newMessage.Header.InteractionType = msgType;

                newMessage.Body = new common.ConnectionEngineMessageBody();
                newMessage.Body.Payload = enginePayload;
            }
            catch (Exception ex)
            {
                throw new common.SystemAdapterException(common.SystemAdapterExceptionCause.CreatingPnPMsg,
                    GENERAL_ERROR_MESSAGE_CREATION, ex);
            }

            return newMessage;
        }

        /// <summary>
        /// Validates if the payload received by the Adapter is a valid payload for the assigned message type.
        /// </summary>
        /// <param name="payload">The payload to be validated.</param>
        /// <param name="messagetypeid">The message type assigned to this payload.</param>
        /// <returns>An indication if the payload is valid or not.</returns>
        protected bool ValidatePayloadSchema(System.Xml.XmlDocument payload, int messagetypeid)
        {
            dao.MessageType msgType = RefData.MessageTypes.Find(delegate(dao.MessageType node)
            {
                if (node.MessageTypeID == messagetypeid)
                    return true;
                else
                    return false;
            });
            if (msgType == null)
            {
                throw new common.SystemAdapterException(common.SystemAdapterExceptionCause.PayloadFormatNotValid,
                                                    "There's no message type associated to the value presented.", null);
            }
            // this is a temporary solution for a problem holding up the validation of ReferenceData
            else if (msgType.MessageTypeID == 1003)
                return true;
            else
            {
                string errormsg = string.Empty;
                if (!common.XmlValidator.ValidateXml(payload, ref errormsg, msgType.XmlSchema))
                {
                    throw new common.SystemAdapterException(common.SystemAdapterExceptionCause.PayloadFormatNotValid,
                                                        "The format of the message payload is invalid according to its message type.", null);
                }
                else
                    return true;
            }
        }

        /// <summary>
        /// Returns a Status Description for a given status code
        /// </summary>
        /// <param name="statuscode">The identifier of the code being retrieved.</param>
        /// <returns>A string representation of the status.</returns>
        protected string GetStatusDescription(int statuscode)
        {
            foreach (dao.MessageStatus msgstatus in RefData.MessageStatuses)
            {
                if (msgstatus.MessageStatusID == statuscode)
                    return msgstatus.DisplayName;
            }
            return string.Empty;
        }

        /// <summary>
        /// Convert a string to a Connection Engine CryptoType
        /// </summary>
        /// <param name="encryptionalgorithm">A string representing an encryption algorithm</param>
        /// <returns>Returns a Connection Engine CryptoType object macthing the encryption algorithm passed as a parameter</returns>
        protected static common.CryptoTypes GetCryptoType(string encryptionalgorithm)
        {
            if (string.IsNullOrEmpty(encryptionalgorithm))
                return common.CryptoTypes.encTypeNone;



            //Work Item ID: 153
            //Modified By: SachinJo
            //Avoiding ToLower call.
            if (string.Compare(encryptionalgorithm, "des", true) == 0)
                return common.CryptoTypes.encTypeDES;
            else if (string.Compare(encryptionalgorithm, "rcs", true) == 0)
                return common.CryptoTypes.encTypeRC2;
            else if (string.Compare(encryptionalgorithm, "rijndael", true) == 0)
                return common.CryptoTypes.encTypeRijndael;
            else if (string.Compare(encryptionalgorithm, "tripledes", true) == 0)
                return common.CryptoTypes.encTypeTripleDES;
            else
                return common.CryptoTypes.encTypeNone;

            //Work Item ID: 153
            //Modified By: SachinJo
            //Code Commented.
            //switch (encryptionalgorithm.ToLower())
            //{
            //    case "des":
            //        return common.CryptoTypes.encTypeDES;
            //    case "rcs":
            //        return common.CryptoTypes.encTypeRC2;
            //    case "rijndael":
            //        return common.CryptoTypes.encTypeRijndael;
            //    case "tripledes":
            //        return common.CryptoTypes.encTypeTripleDES;
            //    default:
            //        return common.CryptoTypes.encTypeNone;
            //}

        }


        /// <summary>
        /// Convert a string to a Connection Engine CryptoType
        /// </summary>
        /// <param name="encryptionalgorithm">A string representing an encryption algorithm</param>
        /// <returns>Returns a Connection Engine CryptoType object macthing the encryption algorithm passed as a parameter</returns>
        protected static common.CryptoTypes GetCryptoType(ws.CryptoTypes encryptionalgorithm)
        {
            switch (encryptionalgorithm)
            {
                case ws.CryptoTypes.encTypeNone:
                    return common.CryptoTypes.encTypeNone;
                case ws.CryptoTypes.encTypeDES:
                    return common.CryptoTypes.encTypeDES;
                case ws.CryptoTypes.encTypeRC2:
                    return common.CryptoTypes.encTypeRC2;
                case ws.CryptoTypes.encTypeRijndael:
                    return common.CryptoTypes.encTypeRijndael;
                case ws.CryptoTypes.encTypeTripleDES:
                    return common.CryptoTypes.encTypeTripleDES;
                default:
                    return common.CryptoTypes.encTypeNone;
            }

        }

        /// <summary>
        /// Convert a string to a Connection Engine Adapter CryptoType
        /// </summary>
        /// <param name="encryptionalgorithm">A string representing an encryption algorithm</param>
        /// <returns>Returns a Connection Engine Adapter CryptoType object macthing the encryption algorithm passed as a parameter</returns>
        internal static ws.CryptoTypes GetWSCryptoType(string encryptionalgorithm)
        {

            if (string.IsNullOrEmpty(encryptionalgorithm))
                return ws.CryptoTypes.encTypeNone;


            //Work Item ID: 153
            //Modified By: SachinJo
            //Avoiding ToLower call.
            if (string.Compare(encryptionalgorithm, "des", true) == 0)
                return ws.CryptoTypes.encTypeDES;
            else if (string.Compare(encryptionalgorithm, "rcs", true) == 0)
                return ws.CryptoTypes.encTypeRC2;
            else if (string.Compare(encryptionalgorithm, "rijndael", true) == 0)
                return ws.CryptoTypes.encTypeRijndael;
            else if (string.Compare(encryptionalgorithm, "tripledes", true) == 0)
                return ws.CryptoTypes.encTypeTripleDES;
            else
                return ws.CryptoTypes.encTypeNone;

            //Work Item ID: 153
            //Modified By: SachinJo
            //Code Commented.
            //switch (encryptionalgorithm.ToLower())
            //{
            //    case "des":
            //        return ws.CryptoTypes.encTypeDES;
            //    case "rcs":
            //        return ws.CryptoTypes.encTypeRC2;
            //    case "rijndael":
            //        return ws.CryptoTypes.encTypeRijndael;
            //    case "tripledes":
            //        return ws.CryptoTypes.encTypeTripleDES;
            //    default:
            //        return ws.CryptoTypes.encTypeNone;
            //}

        }

        /// <summary>
        /// Gets a Connection Engine Adapter Message and transmits it to the Routing Service for processing.
        /// </summary>
        /// <param name="msg">The Connetion Engine Adapter Message being transmited.</param>
        /// <returns>A Connection Engine Adapter Message representing the result of the operation.</returns>
        internal ws.ConnectionEngineMessage SendMessage(ws.ConnectionEngineMessage msg)
        {
            ws.ConnectionEngineAdapter proxy = CreateRoutingServiceProxy();

            proxy.ReceiveConnectionEngineMessage(ref msg);

            return msg;
        }

        /// <summary>
        /// Initialize an instance of the Routing Service Proxy.
        /// This proxy handles all the communication between the Adapter and the Routing Service.
        /// </summary>
        /// <returns>Returns an object representation of the Routing Service Proxy.</returns>
        internal  ws.ConnectionEngineAdapter CreateRoutingServiceProxy()
        {

            ws.ConnectionEngineAdapter proxy = new ws.ConnectionEngineAdapter();
            proxy.Url = connectionEngineWebServiceURL;

            proxy.Proxy = HttpWebRequest.DefaultWebProxy;

            proxy.Proxy.Credentials = System.Net.CredentialCache.DefaultCredentials;
            if (systemAdapterClientCertificate != null)
            {
                X509Certificate cert = common.Crypto.GetX509Certificate(systemAdapterClientCertificate, StoreName.My, StoreLocation.CurrentUser);
                if (cert != null)
                    proxy.ClientCertificates.Add(cert);
            }
            proxy.Credentials = connectionEngineCredentials;

            return proxy;
        }


        #endregion

        #region Private Methods

        /// <summary>
        /// Initializes a Adapter based on the arguments passed as parameter.
        /// </summary>
        /// <param name="connectionenginewebserviceurl">
        /// Represents the Routing Services entry point web service.
        /// </param>
        /// <param name="connectionengineusername">
        /// Represents an optional username to use as credentials when connecting to the Routing Services.
        /// </param>
        /// <param name="connectionenginepassword">
        /// Represents an optional password to use as credentials when connecting to the Routing Services.
        /// </param>
        /// <param name="serviceproviderregisterserviceproviderid">
        /// Represents the unique identifier of the Service Provider Register.
        /// </param>
        /// <param name="serviceproviderregisterencryptiontype">
        /// Represents the encryption type used by Service Provider Register.
        /// </param>
        /// <param name="serviceproviderregisterpublickey">
        /// Represents the public key used to encrypt messages sent to Service Provider Register.
        /// </param>
        /// <param name="systemadapterserviceproviderid">
        /// Represents the unique identifier of the Connected System (also known as the Service Provider) associated with the Adapter.
        /// </param>
        /// <param name="systemadapterwebserviceurl">
        /// An optional string representing the entry point web services used by Adapter.
        /// </param>
        /// <param name="systemadapterencryptionalgorithm">
        /// Represents the encryption algorithm used by the Adapter.
        /// </param>
        /// <param name="systemadapterpublickey">
        /// Represents the public key used by the Adapter.
        /// </param>
        /// <param name="systemadapterprivatekey">
        /// Represents the private key used by the Adapter.
        /// </param>
        /// <param name="systemadapterclientcertificate">
        /// Represents the client certificate that uniquely identifies the Adapter when communicating with the Routing Service through a secure channel.
        /// </param>
        /// <param name="messagehandlerlist">
        /// Represenst The list of message handlers used by the Adapter to process receiving messages.
        /// </param>
        private void Initialize(string connectionenginewebserviceurl,
                                string connectionengineusername, 
                                string connectionenginepassword, 
                                string serviceproviderregisterserviceproviderid, 
                                string serviceproviderregisterencryptiontype, 
                                string serviceproviderregisterpublickey, 
                                string systemadapterserviceproviderid, 
                                string systemadapterwebserviceurl, 
                                string systemadapterencryptionalgorithm, 
                                string systemadapterpublickey, 
                                string systemadapterprivatekey, 
                                string systemadapterclientcertificate, 
                                string messagehandlerlist)
        {
            string error_location = string.Empty;

            try
            {
                connectionEngineWebServiceURL = connectionenginewebserviceurl;
                systemAdapterClientCertificate = systemadapterclientcertificate;

                if (!(String.IsNullOrEmpty(connectionengineusername)) &&
                !(String.IsNullOrEmpty(connectionenginepassword)))
                {
                    connectionEngineCredentials = new System.Net.NetworkCredential(connectionengineusername,connectionenginepassword);
                }
                else
                {
                    connectionEngineCredentials = System.Net.CredentialCache.DefaultNetworkCredentials;
                }

                serviceProviderRegisterServiceProviderID = serviceproviderregisterserviceproviderid;
                serviceProviderRegisterEncryptionType = serviceproviderregisterencryptiontype;
                serviceProviderRegisterPublicKey = common.Utilities.MapPath(serviceproviderregisterpublickey);
                if (messagehandlerlist != null)
                    if (messagehandlerlist.Length > 0) messageHandlerListFile = common.Utilities.MapPath(messagehandlerlist);

                if (systemProvider == null)
                {
                    SystemProvider = new dao.ServiceProvider();

                    SystemProvider.ServiceProviderID = systemadapterserviceproviderid;
                    SystemProvider.WebServiceURL = systemadapterwebserviceurl;
                    SystemProvider.EncryptionAlgorithm = systemadapterencryptionalgorithm;
                    SystemProvider.PublicKey = systemadapterpublickey;
                }
                if (PrivateKey == null)
                {
                    error_location = "Loading Provider Private Key";
                    System.Xml.XmlDocument doc = new System.Xml.XmlDocument();
                    doc.Load(common.Utilities.MapPath(systemadapterprivatekey));
                    PrivateKey = doc.OuterXml;
                }

                if (ProviderList == null)
                {
                    error_location = "Loading Allowed Provider List";
                    ProviderList = GetAllowedProviderList();
                }

                if (RefData == null)
                {
                    error_location = "Loading Reference Data";
                    RefData = GetReferenceData();
                }

                error_location = string.Empty;
            }
            catch (Exception Ex)
            {
                string exception_message;
                if (Ex.InnerException == null)
                    exception_message = Ex.Message;
                else
                    exception_message = Ex.Message + ": " + Ex.InnerException.Message;

                throw new common.SystemAdapterException(common.SystemAdapterExceptionCause.Unknown, "Error found during " + error_location + " - " + exception_message, Ex);
            }
        }
        
        /// <summary>
        /// Translates a Connection Engine Adapter message to a Connection Engine message.
        /// </summary>
        /// <param name="wsmessage">The Connection Engine Adapter message passed as a parameter</param>
        /// <returns>Returns a Connection Engine Message with all the data contained within the Connection Engine Adapter message.</returns>
        private common.ConnectionEngineMessage ConvertWSMessageToCommonMessage(ws.ConnectionEngineMessage wsmessage)
        {
            common.ConnectionEngineMessage result = new common.ConnectionEngineMessage();
            result.Header = new common.ConnectionEngineMessageHeader();

            result.Header.Conversation = new common.ConnectionEngineConversation();
            result.Header.Conversation.ConversationID = wsmessage.Header.Conversation.ConversationID;
            result.Header.Conversation.OriginalMessageID = wsmessage.Header.Conversation.OriginalMessageID;

            result.Header.CreationTime = wsmessage.Header.CreationTime;
            result.Header.ID = wsmessage.Header.ID;
            result.Header.InteractionClass = wsmessage.Header.InteractionClass;
            result.Header.InteractionType = wsmessage.Header.InteractionType;

            result.Header.Receiver = new common.ConnectionEngineEndPoint();
            result.Header.Receiver.ConnectionEngineUserID = wsmessage.Header.Receiver.ConnectionEngineUserID;
            result.Header.Receiver.OrganisationID = wsmessage.Header.Receiver.OrganisationID;
            result.Header.Receiver.OrganisationUserID = wsmessage.Header.Receiver.OrganisationUserID;
            result.Header.Receiver.URI = wsmessage.Header.Receiver.URI;

            result.Header.Sender = new common.ConnectionEngineEndPoint();
            result.Header.Sender.ConnectionEngineUserID = wsmessage.Header.Sender.ConnectionEngineUserID;
            result.Header.Sender.OrganisationID = wsmessage.Header.Sender.OrganisationID;
            result.Header.Sender.OrganisationUserID = wsmessage.Header.Sender.OrganisationUserID;
            result.Header.Sender.URI = wsmessage.Header.Sender.URI;

            result.Header.SessionToken = wsmessage.Header.SessionToken;

            result.Header.Status = new common.ConnectionEngineStatus();
            result.Header.Status.Code = wsmessage.Header.Status.Code;
            result.Header.Status.Description = wsmessage.Header.Status.Description;
            result.Header.Status.Details = wsmessage.Header.Status.Details;

            result.Body.Payload = new common.ConnectionEnginePayload();
            result.Body.Payload.Content = wsmessage.Body.Payload.Content;
            result.Body.Payload.EncryptionType = GetCryptoType(wsmessage.Body.Payload.EncryptionType);
            result.Body.Payload.IV = wsmessage.Body.Payload.IV;
            result.Body.Payload.Key = wsmessage.Body.Payload.Key;

            return result;
        }

        /// <summary>
        /// Defines if the message type passed as parameter is a Change Notification message.
        /// </summary>
        /// <param name="iteractiontype">The unique identifier of the message type.</param>
        /// <returns>Returns a boolean indicating if the message is a Change Notification message or not.</returns>
        private bool IsNotificationMessage(int iteractiontype)
        {
            //TODO: Create proper test for Internal Messages</remarks>
            if (iteractiontype == 9020)
                return true;
            else
                return false;
        }

        /// <summary>
        /// Process a Change Notification message.
        /// </summary>
        /// <param name="payloadXML">The Change Notificaion payload indicating which cached elements should be cleaned up.</param>
        /// <param name="resultPayloadXml">An XML representation of the returned payload.</param>
        /// <param name="resultInteractionType">A representation of the Message Type for the returned payload.</param>
        private void ProcessNotification(System.Xml.XmlDocument payloadXML, out System.Xml.XmlDocument resultPayloadXml, out int resultInteractionType)
        {
            try
            {
                // The next two lines of code ensures that no matter what the namespace of the message is
                // it will still be used to find the ChangedElement node.
                System.Xml.XmlNamespaceManager nsmgr = new System.Xml.XmlNamespaceManager(payloadXML.NameTable);
                nsmgr.AddNamespace("hce", payloadXML.FirstChild.NamespaceURI);

                string changedElement = payloadXML.SelectSingleNode("//hce:ChangedElement",nsmgr).InnerText;
                switch (changedElement)
                {
                    case "serviceprovider":
                        ProviderList = null;
                        break;
                    case "messagetype":
                        RefData = null;
                        break;
                }

                resultPayloadXml = new System.Xml.XmlDocument();
                resultInteractionType = 0;
            }
            catch (System.Exception ex)
            {
                common.SystemAdapterException sysEx = new common.SystemAdapterException(
                    common.SystemAdapterExceptionCause.PayloadContentNotValid,
                    "An error occurred processing the payload.",
                    ex);
                throw sysEx;
            }

        }
        
        /// <summary>
        /// Finds a Service Provider within the list of Allowed Providers based on the provider ID
        /// </summary>
        /// <param name="providerid">The Unique Identifier of the Provider being searched for</param>
        /// <returns>Returns an object representation of the Service Provider.</returns>
        protected dao.ServiceProvider GetDestinationProvider(string providerid)
        {
            foreach (dao.ServiceProvider provider in ProviderList.PoolList[0].ServiceProviders)
            {
                if (provider.ServiceProviderID == providerid)
                    return provider;
            }
            throw new common.SystemAdapterException(common.SystemAdapterExceptionCause.SourceAndDestinationNotInTheSamePool, "The destination provider is not in the list of providers this Adapter can connect to", null);
        }
        #endregion      
    }

}
